package ormybatis.generate;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import freemarker.template.Configuration;

/**
 * @goal gen
 */
public class GenerateMapper extends AbstractMojo {
	private Log log = getLog();

	private Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
	
	private String url, username, password;
	
	private String tableName;
	private String sqlFilePath;
	
	private String daoPackageName;
	private String domainPackageName;
	
	private String daoClassName;
	private String domainClassName;
	
	private String sql =  "select distinct column_name, column_comment, data_type from information_schema.columns WHERE table_name = '{}' order by ORDINAL_POSITION";
	
	private String tableCommentSql =  "SELECT table_comment FROM information_schema.TABLES WHERE TABLE_NAME = '{}' limit 1";
	
	public void init() throws IOException {
		Properties properties = new Properties();
		File filePath = new File(System.getProperty("user.dir") + "/src/main/resources/ormybatis.properties");
		if (!filePath.exists()) {
			filePath = new File(System.getProperty("user.dir") + "/src/test/java/ormybatis.properties");
			if (!filePath.exists()) {
				filePath = new File(System.getProperty("user.dir") + "/src/test/resources/ormybatis.properties");
			}
		}
		if (!filePath.exists()) {
			log.error("没有ormybatis.properties文件");
			return;
		}
		properties.load(new FileInputStream(filePath));
		//cfg.setDirectoryForTemplateLoading(new ClassPathResource("ftl").getFile());//读取工程内路径
		cfg.setClassForTemplateLoading(GenerateMapper.class, "/ftl");//读取jar内路径
		
		url = properties.getProperty("jdbc.url");
		username = properties.getProperty("jdbc.username");
		password = properties.getProperty("jdbc.password");
		
		tableName = properties.getProperty("table.name").trim();
		sqlFilePath = properties.getProperty("sqlFile.path");
		
		daoPackageName = properties.getProperty("dao.package.path");
		domainPackageName = properties.getProperty("domain.package.path");
		
		daoClassName = properties.getProperty("dao.class.name");
		domainClassName = properties.getProperty("domain.class.name");
	}
	
	@Override
	public void execute() throws MojoExecutionException, MojoFailureException{
		try {
			this.init();
		} catch (IOException e) {
			log.error(e.getMessage());
			return;
		}
		List<Entity> list = this.selectList(StrUtil.format(this.sql, this.tableName) );
		
		//设置Dao类名
		if (StrUtil.isBlank(daoClassName)) {
			daoClassName = toUpperCamel(tableName).trim() + "Dao";
		}
		
		//设置domain bean类史
		if (StrUtil.isBlank(domainClassName)) {
			domainClassName = toUpperCamel(tableName).trim();
		}
		
		Map dataModel = new HashMap();
		dataModel.put("tableName", tableName);
		dataModel.put("daoClassName", daoClassName);
		dataModel.put("domainClassName", domainClassName);
		dataModel.put("namespace", toLowerCamel(tableName));
		this.data(list);
		dataModel.put("fieldList", list);
		
		dataModel.put("daoPackageName", daoPackageName);
		dataModel.put("domainPackageName", domainPackageName);
		//得到表名的注解
		Entity commentEntity = this.selectList(StrUtil.format(this.tableCommentSql, this.tableName) ).get(0);
		String tableComment = commentEntity.getStr("table_comment");
		dataModel.put("tableNameComment", tableComment);
		
		String userProjectDir = System.getProperty("user.dir");
		
		String daoPath = userProjectDir +"/src/main/java/" + StrUtil.replace(daoPackageName, ".", "/") + "/";
		String daoFileName = daoPath + daoClassName + ".java";
		newFilByTemaplate(dataModel, "dao", daoFileName);
		
		String beanPath = userProjectDir +"/src/main/java/" + StrUtil.replace(domainPackageName, ".", "/") + "/";
		String beanFileName = beanPath + domainClassName + ".java";
		newFilByTemaplate(dataModel, "bean", beanFileName);
		
		String mapperPath = userProjectDir +"/src/main/resources/" + sqlFilePath + "/";
		String mapperFileName = mapperPath + toLowerCamel(domainClassName) + ".xml";
		newFilByTemaplate(dataModel, "mapper", mapperFileName);
		
		log.info("成功请刷新工程");
	}

	private void newFilByTemaplate(Map dataModel, String ftlFileName, String fileName){
		if(new File(fileName).exists()){
			log.warn(fileName + "已经存在");
		} else {
			TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("ftl", ResourceMode.CLASSPATH));
			engine.getTemplate(ftlFileName + ".ftl").render(dataModel, new File(fileName));
			log.info(fileName + "成功生成");
		}
	}
	
	private void data(List<Entity> list) {
		for (Entity entity : list) {
			String dbFieldName = entity.getStr("column_name");
			String type = entity.getStr("data_type");
			String javaType = "";
			if (StrUtil.containsIgnoreCase(type, "int")) {
				javaType = "Integer";
			} else if (StrUtil.containsAnyIgnoreCase(type, "double", "float", "decimal")) {
				javaType = "BigDecimal";
			} else if (StrUtil.containsAnyIgnoreCase(type, "varchar", "text", "char", "json")) {
				javaType = "String";
			} else if (StrUtil.containsAnyIgnoreCase(type, "date", "timestamp")) {
				javaType = "Date";
			}
			entity.put("type", javaType);
			String propertyName = toLowerCamel(dbFieldName);
			entity.put("propertyName", propertyName);
			entity.put("comment", entity.getStr("column_comment"));
			entity.put("underlineCasePropertyName", toLowerUnderscore(propertyName).toUpperCase());
		}
	}

	private  List<Entity> selectList(String sql) {
		SimpleDataSource ds = new SimpleDataSource(url, username, password);
		try {
			return Db.use(ds).query(sql);
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * <pre>
	 * AbcXyz == abc_xyz
	 * abcXyz == abc_xyz
	 * abcXyz1 == abc_xyz1
	 * </pre>
	 */
	private static String toLowerUnderscore(String s) {
		//如果全大写和数字
		if (ReUtil.isMatch("[A-Z0-9]+", s)) {
			return s.toLowerCase();
		}
		return StrUtil.toUnderlineCase(s);
	}

	/**
	 * <pre>
	 *	abcXyz == AbcXyz
	 * abc_xyz == AbcXyz
	 * </pre>
	 */
	private static String toUpperCamel(String s) {
		s = toLowerCamel(s);
		return StrUtil.upperFirst(s);
	}
	
	/**
	 * <pre>
	 * AbcXyz == abcXyz
	 * abc_xyz == abcXyz
	 * </pre>
	 */
	private static String toLowerCamel(String s) {
		//如果全大写和数字
		if (ReUtil.isMatch("[A-Z0-9]+", s)) {
			s = s.toLowerCase();
		}
		return StrUtil.toCamelCase(s);
	}
}